{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# LangChain: Models, Prompts and Output Parsers\n",
    "\n",
    "\n",
    "## Outline\n",
    "\n",
    " * Direct API calls to OpenAI\n",
    " * API calls through LangChain:\n",
    "   * Prompts\n",
    "   * Models\n",
    "   * Output parsers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:01:48.659480Z",
     "end_time": "2023-06-13T17:01:50.581839Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: http://mirrors.aliyun.com/pypi/simple/\r\n",
      "Collecting python-dotenv\r\n",
      "  Downloading http://mirrors.aliyun.com/pypi/packages/44/2f/62ea1c8b593f4e093cc1a7768f0d46112107e790c3e478532329e434f00b/python_dotenv-1.0.0-py3-none-any.whl (19 kB)\r\n",
      "Installing collected packages: python-dotenv\r\n",
      "Successfully installed python-dotenv-1.0.0\r\n",
      "\r\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.0.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m23.1.2\u001B[0m\r\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpip3 install --upgrade pip\u001B[0m\r\n",
      "Looking in indexes: http://mirrors.aliyun.com/pypi/simple/\r\n",
      "Requirement already satisfied: openai in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (0.27.4)\r\n",
      "Requirement already satisfied: tqdm in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from openai) (4.65.0)\r\n",
      "Requirement already satisfied: aiohttp in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from openai) (3.8.4)\r\n",
      "Requirement already satisfied: requests>=2.20 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from openai) (2.30.0)\r\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from requests>=2.20->openai) (3.1.0)\r\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from requests>=2.20->openai) (2022.12.7)\r\n",
      "Requirement already satisfied: urllib3<3,>=1.21.1 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from requests>=2.20->openai) (1.26.15)\r\n",
      "Requirement already satisfied: idna<4,>=2.5 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from requests>=2.20->openai) (3.4)\r\n",
      "Requirement already satisfied: yarl<2.0,>=1.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp->openai) (1.9.1)\r\n",
      "Requirement already satisfied: frozenlist>=1.1.1 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp->openai) (1.3.3)\r\n",
      "Requirement already satisfied: multidict<7.0,>=4.5 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp->openai) (6.0.4)\r\n",
      "Requirement already satisfied: aiosignal>=1.1.2 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp->openai) (1.3.1)\r\n",
      "Requirement already satisfied: async-timeout<5.0,>=4.0.0a3 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp->openai) (4.0.2)\r\n",
      "Requirement already satisfied: attrs>=17.3.0 in /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages (from aiohttp->openai) (23.1.0)\r\n",
      "\r\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.0.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m23.1.2\u001B[0m\r\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpip3 install --upgrade pip\u001B[0m\r\n"
     ]
    }
   ],
   "source": [
    "# !pip install python-dotenv\n",
    "# !pip install openai"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-14T14:43:17.522579Z",
     "end_time": "2023-06-14T14:43:17.757082Z"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import openai\n",
    "\n",
    "from dotenv import load_dotenv, find_dotenv\n",
    "_ = load_dotenv(find_dotenv()) # read local .env file\n",
    "openai.api_key = os.environ['OPENAI_API_KEY']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "outputs": [
    {
     "data": {
      "text/plain": "'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'"
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "openai.api_key"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "start_time": "2023-06-14T14:43:17.761072Z",
     "end_time": "2023-06-14T14:43:17.767268Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Chat API : OpenAI\n",
    "\n",
    "Let's start with a direct API calls to OpenAI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:05:51.747982Z",
     "end_time": "2023-06-13T17:05:51.757232Z"
    }
   },
   "outputs": [],
   "source": [
    "def get_completion(prompt, model=\"gpt-3.5-turbo\"):\n",
    "    messages = [{\"role\": \"user\", \"content\": prompt}]\n",
    "    response = openai.ChatCompletion.create(\n",
    "        model=model,\n",
    "        messages=messages,\n",
    "        temperature=0, \n",
    "    )\n",
    "    return response.choices[0].message[\"content\"]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:05:55.809357Z",
     "end_time": "2023-06-13T17:05:58.766101Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "'As an AI language model, I can tell you that the answer to 1+1 is 2.'"
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "get_completion(\"What is 1+1?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:06:02.978508Z",
     "end_time": "2023-06-13T17:06:02.983413Z"
    }
   },
   "outputs": [],
   "source": [
    "customer_email = \"\"\"\n",
    "Arrr, I be fuming that me blender lid \\\n",
    "flew off and splattered me kitchen walls \\\n",
    "with smoothie! And to make matters worse,\\\n",
    "the warranty don't cover the cost of \\\n",
    "cleaning up me kitchen. I need yer help \\\n",
    "right now, matey!\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:06:04.271556Z",
     "end_time": "2023-06-13T17:06:04.285175Z"
    }
   },
   "outputs": [],
   "source": [
    "style = \"\"\"American English \\\n",
    "in a calm and respectful tone\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:06:09.319055Z",
     "end_time": "2023-06-13T17:06:09.322356Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Translate the text that is delimited by triple backticks \n",
      "into a style that is American English in a calm and respectful tone\n",
      ".\n",
      "text: ```\n",
      "Arrr, I be fuming that me blender lid flew off and splattered me kitchen walls with smoothie! And to make matters worse,the warranty don't cover the cost of cleaning up me kitchen. I need yer help right now, matey!\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "prompt = f\"\"\"Translate the text \\\n",
    "that is delimited by triple backticks \n",
    "into a style that is {style}.\n",
    "text: ```{customer_email}```\n",
    "\"\"\"\n",
    "\n",
    "print(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:06:16.406313Z",
     "end_time": "2023-06-13T17:06:19.082884Z"
    }
   },
   "outputs": [],
   "source": [
    "response = get_completion(prompt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:06:19.980147Z",
     "end_time": "2023-06-13T17:06:19.987205Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "'I am quite upset that my blender lid came off and caused my smoothie to splatter all over my kitchen walls. Additionally, the warranty does not cover the cost of cleaning up the mess. Would you be able to assist me at this time, my friend?'"
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Chat API : LangChain\n",
    "\n",
    "Let's try how we can do the same using LangChain."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "#!pip install --upgrade langchain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:07:15.784336Z",
     "end_time": "2023-06-13T17:07:16.587120Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain.chat_models import ChatOpenAI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:07:18.249177Z",
     "end_time": "2023-06-13T17:07:18.257118Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "ChatOpenAI(lc_kwargs={'temperature': 0.0}, verbose=False, callbacks=None, callback_manager=None, client=<class 'openai.api_resources.chat_completion.ChatCompletion'>, model_name='gpt-3.5-turbo', temperature=0.0, model_kwargs={}, openai_api_key='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', openai_api_base='', openai_organization='', openai_proxy='', request_timeout=None, max_retries=6, streaming=False, n=1, max_tokens=None)"
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# To control the randomness and creativity of the generated\n",
    "# text by an LLM, use temperature = 0.0\n",
    "chat = ChatOpenAI(temperature=0.0)\n",
    "chat"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Prompt template"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:07:43.856063Z",
     "end_time": "2023-06-13T17:07:43.858857Z"
    }
   },
   "outputs": [],
   "source": [
    "template_string = \"\"\"Translate the text \\\n",
    "that is delimited by triple backticks \\\n",
    "into a style that is {style}. \\\n",
    "text: ```{text}```\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:07:52.896263Z",
     "end_time": "2023-06-13T17:07:52.899579Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "prompt_template = ChatPromptTemplate.from_template(template_string)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:08:41.791748Z",
     "end_time": "2023-06-13T17:08:41.803880Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "PromptTemplate(lc_kwargs={'input_variables': ['style', 'text'], 'template': 'Translate the text that is delimited by triple backticks into a style that is {style}. text: ```{text}```\\n'}, input_variables=['style', 'text'], output_parser=None, partial_variables={}, template='Translate the text that is delimited by triple backticks into a style that is {style}. text: ```{text}```\\n', template_format='f-string', validate_template=True)"
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt_template.messages[0].prompt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:08:45.628672Z",
     "end_time": "2023-06-13T17:08:45.631875Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "['style', 'text']"
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt_template.messages[0].prompt.input_variables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:08:53.606591Z",
     "end_time": "2023-06-13T17:08:53.609028Z"
    }
   },
   "outputs": [],
   "source": [
    "customer_style = \"\"\"American English \\\n",
    "in a calm and respectful tone\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:08:55.367563Z",
     "end_time": "2023-06-13T17:08:55.372043Z"
    }
   },
   "outputs": [],
   "source": [
    "customer_email = \"\"\"\n",
    "Arrr, I be fuming that me blender lid \\\n",
    "flew off and splattered me kitchen walls \\\n",
    "with smoothie! And to make matters worse, \\\n",
    "the warranty don't cover the cost of \\\n",
    "cleaning up me kitchen. I need yer help \\\n",
    "right now, matey!\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:08:58.791106Z",
     "end_time": "2023-06-13T17:08:58.793832Z"
    }
   },
   "outputs": [],
   "source": [
    "customer_messages = prompt_template.format_messages(\n",
    "                    style=customer_style,\n",
    "                    text=customer_email)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:09:01.528445Z",
     "end_time": "2023-06-13T17:09:01.532890Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'list'>\n",
      "<class 'langchain.schema.HumanMessage'>\n"
     ]
    }
   ],
   "source": [
    "print(type(customer_messages))\n",
    "print(type(customer_messages[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:09:04.191849Z",
     "end_time": "2023-06-13T17:09:04.206282Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lc_kwargs={'content': \"Translate the text that is delimited by triple backticks into a style that is American English in a calm and respectful tone\\n. text: ```\\nArrr, I be fuming that me blender lid flew off and splattered me kitchen walls with smoothie! And to make matters worse, the warranty don't cover the cost of cleaning up me kitchen. I need yer help right now, matey!\\n```\\n\", 'additional_kwargs': {}} content=\"Translate the text that is delimited by triple backticks into a style that is American English in a calm and respectful tone\\n. text: ```\\nArrr, I be fuming that me blender lid flew off and splattered me kitchen walls with smoothie! And to make matters worse, the warranty don't cover the cost of cleaning up me kitchen. I need yer help right now, matey!\\n```\\n\" additional_kwargs={} example=False\n"
     ]
    }
   ],
   "source": [
    "print(customer_messages[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:09:14.223514Z",
     "end_time": "2023-06-13T17:09:16.980737Z"
    }
   },
   "outputs": [],
   "source": [
    "# Call the LLM to translate to the style of the customer message\n",
    "customer_response = chat(customer_messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:09:16.982023Z",
     "end_time": "2023-06-13T17:09:16.985571Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I'm really frustrated that my blender lid flew off and made a mess of my kitchen walls with smoothie. To add to my frustration, the warranty doesn't cover the cost of cleaning up my kitchen. Can you please help me out, friend?\n"
     ]
    }
   ],
   "source": [
    "print(customer_response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:10:44.712207Z",
     "end_time": "2023-06-13T17:10:44.716461Z"
    }
   },
   "outputs": [],
   "source": [
    "service_reply = \"\"\"Hey there customer, \\\n",
    "the warranty does not cover \\\n",
    "cleaning expenses for your kitchen \\\n",
    "because it's your fault that \\\n",
    "you misused your blender \\\n",
    "by forgetting to put the lid on before \\\n",
    "starting the blender. \\\n",
    "Tough luck! See ya!\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:10:47.058156Z",
     "end_time": "2023-06-13T17:10:47.067637Z"
    }
   },
   "outputs": [],
   "source": [
    "service_style_pirate = \"\"\"\\\n",
    "a polite tone \\\n",
    "that speaks in English Pirate\\\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:10:49.887801Z",
     "end_time": "2023-06-13T17:10:49.891019Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Translate the text that is delimited by triple backticks into a style that is a polite tone that speaks in English Pirate. text: ```Hey there customer, the warranty does not cover cleaning expenses for your kitchen because it's your fault that you misused your blender by forgetting to put the lid on before starting the blender. Tough luck! See ya!\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "service_messages = prompt_template.format_messages(\n",
    "    style=service_style_pirate,\n",
    "    text=service_reply)\n",
    "\n",
    "print(service_messages[0].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:10:53.382557Z",
     "end_time": "2023-06-13T17:10:58.046880Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Ahoy there, me hearty customer! I be sorry to inform ye that the warranty be not coverin' the expenses o' cleaning yer galley, as 'tis yer own fault fer misusin' yer blender by forgettin' to put the lid on afore startin' it. Aye, tough luck! Farewell and may the winds be in yer favor!\n"
     ]
    }
   ],
   "source": [
    "service_response = chat(service_messages)\n",
    "print(service_response.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Output Parsers\n",
    "\n",
    "Let's start with defining how we would like the LLM output to look like:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:11:24.663510Z",
     "end_time": "2023-06-13T17:11:24.687899Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "{'gift': False, 'delivery_days': 5, 'price_value': 'pretty affordable!'}"
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "{\n",
    "  \"gift\": False,\n",
    "  \"delivery_days\": 5,\n",
    "  \"price_value\": \"pretty affordable!\"\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:11:40.508648Z",
     "end_time": "2023-06-13T17:11:40.511308Z"
    }
   },
   "outputs": [],
   "source": [
    "customer_review = \"\"\"\\\n",
    "This leaf blower is pretty amazing.  It has four settings:\\\n",
    "candle blower, gentle breeze, windy city, and tornado. \\\n",
    "It arrived in two days, just in time for my wife's \\\n",
    "anniversary present. \\\n",
    "I think my wife liked it so much she was speechless. \\\n",
    "So far I've been the only one using it, and I've been \\\n",
    "using it every other morning to clear the leaves on our lawn. \\\n",
    "It's slightly more expensive than the other leaf blowers \\\n",
    "out there, but I think it's worth it for the extra features.\n",
    "\"\"\"\n",
    "\n",
    "review_template = \"\"\"\\\n",
    "For the following text, extract the following information:\n",
    "\n",
    "gift: Was the item purchased as a gift for someone else? \\\n",
    "Answer True if yes, False if not or unknown.\n",
    "\n",
    "delivery_days: How many days did it take for the product \\\n",
    "to arrive? If this information is not found, output -1.\n",
    "\n",
    "price_value: Extract any sentences about the value or price,\\\n",
    "and output them as a comma separated Python list.\n",
    "\n",
    "Format the output as JSON with the following keys:\n",
    "gift\n",
    "delivery_days\n",
    "price_value\n",
    "\n",
    "text: {text}\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:11:48.634307Z",
     "end_time": "2023-06-13T17:11:48.639900Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lc_kwargs={'input_variables': ['text'], 'messages': [HumanMessagePromptTemplate(lc_kwargs={'prompt': PromptTemplate(lc_kwargs={'input_variables': ['text'], 'template': 'For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n'}, input_variables=['text'], output_parser=None, partial_variables={}, template='For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n', template_format='f-string', validate_template=True)}, prompt=PromptTemplate(lc_kwargs={'input_variables': ['text'], 'template': 'For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n'}, input_variables=['text'], output_parser=None, partial_variables={}, template='For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n', template_format='f-string', validate_template=True), additional_kwargs={})]} input_variables=['text'] output_parser=None partial_variables={} messages=[HumanMessagePromptTemplate(lc_kwargs={'prompt': PromptTemplate(lc_kwargs={'input_variables': ['text'], 'template': 'For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n'}, input_variables=['text'], output_parser=None, partial_variables={}, template='For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n', template_format='f-string', validate_template=True)}, prompt=PromptTemplate(lc_kwargs={'input_variables': ['text'], 'template': 'For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n'}, input_variables=['text'], output_parser=None, partial_variables={}, template='For the following text, extract the following information:\\n\\ngift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\\n\\ndelivery_days: How many days did it take for the product to arrive? If this information is not found, output -1.\\n\\nprice_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\\n\\nFormat the output as JSON with the following keys:\\ngift\\ndelivery_days\\nprice_value\\n\\ntext: {text}\\n', template_format='f-string', validate_template=True), additional_kwargs={})]\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "prompt_template = ChatPromptTemplate.from_template(review_template)\n",
    "print(prompt_template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:12:01.686972Z",
     "end_time": "2023-06-13T17:12:04.970509Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "    \"gift\": true,\n",
      "    \"delivery_days\": 2,\n",
      "    \"price_value\": [\"It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\"]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "messages = prompt_template.format_messages(text=customer_review)\n",
    "chat = ChatOpenAI(temperature=0.0)\n",
    "response = chat(messages)\n",
    "print(response.content)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:12:06.646630Z",
     "end_time": "2023-06-13T17:12:06.649967Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "str"
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'str' object has no attribute 'get'",
     "output_type": "error",
     "traceback": [
      "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[0;31mAttributeError\u001B[0m                            Traceback (most recent call last)",
      "Cell \u001B[0;32mIn[38], line 4\u001B[0m\n\u001B[1;32m      1\u001B[0m \u001B[38;5;66;03m# You will get an error by running this line of code \u001B[39;00m\n\u001B[1;32m      2\u001B[0m \u001B[38;5;66;03m# because 'gift' is not a dictionary\u001B[39;00m\n\u001B[1;32m      3\u001B[0m \u001B[38;5;66;03m# 'gift' is a string\u001B[39;00m\n\u001B[0;32m----> 4\u001B[0m \u001B[43mresponse\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mcontent\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget\u001B[49m(\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mgift\u001B[39m\u001B[38;5;124m'\u001B[39m)\n",
      "\u001B[0;31mAttributeError\u001B[0m: 'str' object has no attribute 'get'"
     ]
    }
   ],
   "source": [
    "# You will get an error by running this line of code \n",
    "# because 'gift' is not a dictionary\n",
    "# 'gift' is a string\n",
    "response.content.get('gift')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Parse the LLM output string into a Python dictionary"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:13:12.911478Z",
     "end_time": "2023-06-13T17:13:12.921276Z"
    }
   },
   "outputs": [],
   "source": [
    "from langchain.output_parsers import ResponseSchema\n",
    "from langchain.output_parsers import StructuredOutputParser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:13:14.110926Z",
     "end_time": "2023-06-13T17:13:14.117939Z"
    }
   },
   "outputs": [],
   "source": [
    "gift_schema = ResponseSchema(name=\"gift\",\n",
    "                             description=\"Was the item purchased\\\n",
    "                             as a gift for someone else? \\\n",
    "                             Answer True if yes,\\\n",
    "                             False if not or unknown.\")\n",
    "delivery_days_schema = ResponseSchema(name=\"delivery_days\",\n",
    "                                      description=\"How many days\\\n",
    "                                      did it take for the product\\\n",
    "                                      to arrive? If this \\\n",
    "                                      information is not found,\\\n",
    "                                      output -1.\")\n",
    "price_value_schema = ResponseSchema(name=\"price_value\",\n",
    "                                    description=\"Extract any\\\n",
    "                                    sentences about the value or \\\n",
    "                                    price, and output them as a \\\n",
    "                                    comma separated Python list.\")\n",
    "\n",
    "response_schemas = [gift_schema, \n",
    "                    delivery_days_schema,\n",
    "                    price_value_schema]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:13:19.265514Z",
     "end_time": "2023-06-13T17:13:19.268236Z"
    }
   },
   "outputs": [],
   "source": [
    "output_parser = StructuredOutputParser.from_response_schemas(response_schemas)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:13:21.532939Z",
     "end_time": "2023-06-13T17:13:21.540173Z"
    }
   },
   "outputs": [],
   "source": [
    "format_instructions = output_parser.get_format_instructions()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:26:21.653774Z",
     "end_time": "2023-06-13T17:26:21.657819Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The output should be a markdown code snippet formatted in the following schema, including the leading and trailing \"```json\" and \"```\":\n",
      "\n",
      "```json\n",
      "{\n",
      "\t\"gift\": string  // Was the item purchased                             as a gift for someone else?                              Answer True if yes,                             False if not or unknown.\n",
      "\t\"delivery_days\": string  // How many days                                      did it take for the product                                      to arrive? If this                                       information is not found,                                      output -1.\n",
      "\t\"price_value\": string  // Extract any                                    sentences about the value or                                     price, and output them as a                                     comma separated Python list.\n",
      "}\n",
      "```\n"
     ]
    }
   ],
   "source": [
    "print(format_instructions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "review_template_2 = \"\"\"\\\n",
    "For the following text, extract the following information:\n",
    "\n",
    "gift: Was the item purchased as a gift for someone else? \\\n",
    "Answer True if yes, False if not or unknown.\n",
    "\n",
    "delivery_days: How many days did it take for the product\\\n",
    "to arrive? If this information is not found, output -1.\n",
    "\n",
    "price_value: Extract any sentences about the value or price,\\\n",
    "and output them as a comma separated Python list.\n",
    "\n",
    "text: {text}\n",
    "\n",
    "{format_instructions}\n",
    "\"\"\"\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(template=review_template_2)\n",
    "\n",
    "messages = prompt.format_messages(text=customer_review, \n",
    "                                format_instructions=format_instructions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "For the following text, extract the following information:\n",
      "\n",
      "gift: Was the item purchased as a gift for someone else? Answer True if yes, False if not or unknown.\n",
      "\n",
      "delivery_days: How many days did it take for the productto arrive? If this information is not found, output -1.\n",
      "\n",
      "price_value: Extract any sentences about the value or price,and output them as a comma separated Python list.\n",
      "\n",
      "text: This leaf blower is pretty amazing.  It has four settings:candle blower, gentle breeze, windy city, and tornado. It arrived in two days, just in time for my wife's anniversary present. I think my wife liked it so much she was speechless. So far I've been the only one using it, and I've been using it every other morning to clear the leaves on our lawn. It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\n",
      "\n",
      "\n",
      "The output should be a markdown code snippet formatted in the following schema, including the leading and trailing \"\\`\\`\\`json\" and \"\\`\\`\\`\":\n",
      "\n",
      "```json\n",
      "{\n",
      "\t\"gift\": string  // Was the item purchased                             as a gift for someone else?                              Answer True if yes,                             False if not or unknown.\n",
      "\t\"delivery_days\": string  // How many days                                      did it take for the product                                      to arrive? If this                                       information is not found,                                      output -1.\n",
      "\t\"price_value\": string  // Extract any                                    sentences about the value or                                     price, and output them as a                                     comma separated Python list.\n",
      "}\n",
      "```\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(messages[0].content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:26:41.399624Z",
     "end_time": "2023-06-13T17:26:44.998647Z"
    }
   },
   "outputs": [],
   "source": [
    "response = chat(messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:26:45.832042Z",
     "end_time": "2023-06-13T17:26:45.836548Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "    \"gift\": true,\n",
      "    \"delivery_days\": 2,\n",
      "    \"price_value\": [\"It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\"]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "print(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:26:56.663755Z",
     "end_time": "2023-06-13T17:26:56.666284Z"
    }
   },
   "outputs": [],
   "source": [
    "output_dict = output_parser.parse(response.content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:26:57.842879Z",
     "end_time": "2023-06-13T17:26:57.846923Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "{'gift': True,\n 'delivery_days': 2,\n 'price_value': [\"It's slightly more expensive than the other leaf blowers out there, but I think it's worth it for the extra features.\"]}"
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:26:59.976487Z",
     "end_time": "2023-06-13T17:26:59.980338Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "dict"
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(output_dict)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "ExecuteTime": {
     "start_time": "2023-06-13T17:27:02.565392Z",
     "end_time": "2023-06-13T17:27:02.568631Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "2"
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_dict.get('delivery_days')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
